package edu.amm.fanal.api;

/**
 * Преобразование к задаче с нулевыми граничными условиями.
 * 
 * @author Иванов Илья
 * @since 2013-05-11
 *
 */
public class ZeroTransformation extends Transformation {
	
	private Function transformFunction;
	
	/**
	 * @param sourceProblem Исходная задача.
	 */
	public ZeroTransformation(LinearProblem sourceProblem) {
		super(sourceProblem);
	}
	
	protected LinearProblem transform() {
		final double a = sourceProblem.getLeftBound();
		final double b = sourceProblem.getRightBound();
		final double alpha = sourceProblem.getLeftValue();
		final double beta = sourceProblem.getRightValue();
		final double coef = (beta - alpha) / (b - a);
		
		transformFunction = new Function(BoundaryProblem.SOLUTION_DIM) {
			
			private Function derivative = Function.createIdentical(getDim(), coef);
			
			protected double getValue(double... x) {
				return coef * (x[0] - a) + alpha;
			}
			
			protected Function getDerivative(int variableNumber) {
				return derivative;
			}
		};
		
		final Function p = sourceProblem.getP();
		final Function q = sourceProblem.getQ();
		final Function f = sourceProblem.getF();
		final Function h = transformFunction;
		final Function dhdt = transformFunction.differentiate(0);
		
		Function rightHand = new Function(BoundaryProblem.SOLUTION_DIM) {
			
			protected double getValue(double... x) {
				return f.evaluate(x) - p.evaluate(x) * dhdt.evaluate(x) - q.evaluate(x) * h.evaluate(x);
			}
		};
		
		LinearProblem transformed = new LinearProblem(a, 0, b, 0);
		transformed.setP(p);
		transformed.setQ(q);
		transformed.setF(rightHand);
		
		return transformed;
	}
	
	protected Function restore(final Function solution) {
		return new Function(BoundaryProblem.SOLUTION_DIM) {
			
			private Function derivative = new Function(getDim()) {
				
				private Function dydt = solution.differentiate(0);
				private Function dhdt = transformFunction.differentiate(0);
				
				protected double getValue(double... x) {
					return dydt.evaluate(x) + dhdt.evaluate(x);
				}
			};
			
			protected double getValue(double... x) {
				return solution.evaluate(x) + transformFunction.evaluate(x);
			}
			
			protected Function getDerivative(int variableNumber) {
				return derivative;
			}
		};
	}
}